package core.java;


import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * @Auther: ssk
 * @Date: 2019/1/18 17:06
 * @Description: 泛型
 * 参考 https://blog.csdn.net/wisgood/article/details/11762427
 */
public class Genericity<T> {
    private  T  frist;
    private T  second;
    private T[] element;
    public Genericity(){

    }
    //Supplier<T>   get方法 获取一个T对象
    //Function<T,U> apply 传入一个T对象,返回一个U对象
    // 调用 Genericity<ClassName>(ClassName::new,ClassName::new)
    public Genericity(Supplier<T> supplier,Function<Integer[],T[]> function) {
        frist = supplier.get();
        second = supplier.get();
        element = function.apply(new Integer[3]);
    }
    //反射方法
    public Genericity(Class<T> cl) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        frist = cl.getDeclaredConstructor().newInstance();
        second = cl.getDeclaredConstructor().newInstance();
        element = (T[]) Array.newInstance(cl.getClass(),0);
    }

    public T getFrist() {
        return frist;
    }

    public void setFrist(T frist) {
        this.frist = frist;
    }

    public T getSecond() {
        return second;
    }

    public void setSecond(T second) {
        this.second = second;
    }

    @Override
    public String toString() {
        return "Genericity{" +
                "frist=" + frist +
                ", second=" + second +
                '}';
    }

    public static <U extends Comparable> U maxx(U... obj){
        assert obj.length>=0;
         U maxn = obj[0];
        for (int i = 1; i < obj.length; i++) {
            if (obj[i].compareTo(maxn)>0){
                maxn=obj[i];
            }
        }
        return maxn;
    }

    public static void main(String args[]) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
       System.out.println(Genericity.maxx(3,4,5,6,8));
       GenericityChild genericityChild = new GenericityChild();
       Genericity<String> genericity = genericityChild;
       genericity.setFrist("frist");
       genericity.setSecond("second");
       System.out.println(genericity);
       System.out.println(genericity.getSecond());

       //通过函数式编程
       Genericity<String> genericity1 = new Genericity<String>(String::new,(b)->{
           String temp[] = new String[b.length];
           for(int i=0;i<b.length;i++)
               temp[i]=b.toString();
           return temp;
       });
       //反射
       Genericity<String> genericity2 = new Genericity<String>(String.class);
       GenericityChild.swap(genericity);
        try {
            ReflectionStudy.init("core.java.GenericityChild");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }


    }
}

/**
 * 继承 Genericity 看下泛型类的擦除
 * <T extends ClassName1 & ClassName2> 会擦除为第一个类 如何没有规定范围就是object
 * 泛型翻译表达式  1 调用原始的对象(擦除后的)返回一个object 2 对object进行强制转换
 * 翻译方法 当有个类(child)继承泛型对象并且重写方法后,child会生成两个相同的方法,比如下面会生成
 *         public void setSecond(String second) 和 public void setSecond(Object second) 那么当使用多态时会产生一个问题
 *
 *          GenericityChild genericityChild = new GenericityChild();
 *          Genericity<String> genericity = genericityChild;
 *          调用genericity方法的setSecond时应该调用的setSecond(Object second) 但是我们想要的不是它,这点编译器使用了桥方法
 *          public void setSecond(Object second) {
 *              setSecond((String)second);
 *          }
 *          同理get方法返回也是类似
 *  不能构建参数化的泛型数组 例如 Genericity<String> genericity[] = new Genericity<String>[10]; error
 *          这是因为类型擦除 就例如上面的Genericity<String>  genericity 擦除后成为了object[]类型,
 *          这样可以直接genericity[0]=new Genericity()(因为擦除后成为了object[]) 但是当我们用的时候都是强制转换的,所以会错
 *          编译器就禁止该行为,直接报编译错误,但是Genericity<String> genericity[] 是正确的就是不能直接 new 一个泛型数组
 *  varargs 可变参数 泛型不能建数组,但是用可变参数就是组装成一个数组,这点在规则上允许,但会产生警告可用@SafeVarars取消
 * 不能实例化泛型 new T 有两种方法使用 看17-29行
 * 不能实例化泛型数组 new T[] 看 17 - 29行
 * 使用看 80-87行
 * 禁止使用静态泛型方法和属性
 * 注意擦除后的方法从图 boolean equals(T);擦除后成了 boolean equals(object); 会冲突
 * 泛型原则 必须强制限制一个类或类型变量不能同时成为两个接口的子类，而这两个接口是同一接口的不同参数化
 *         如 class A implement C<T> {....}   class B  extends A implement C<U>{....}   T和U是不同类型 再合成桥方法1时会冲突
 * 泛型无继承
 * 通配符 <? extends T> <? super T> <?>
 *     <? extends T> 只可取T以及T的子类   利用这个我们只知道要存T的子类,但不知道要存取的是什么,要取数据时我们都可以转化为T(因为都是T的子类)
 *     <? super T> 只可存T以及T的子类  利用这个我们在存T子类以及T时都能转化为T 但取的时候不知道确定类型所以都会转化为object
 *     <?> 一般用于判空一类的操作 既不能取也不能存
 *     PECS法则
 *          1. 如果你想在方法中从input参数里获取数据，使用<? extends T>通配符
 *          2. 如果你想在方法中把对象写入一个input参数中，使用<? super T>通配符
 *          3. 如果你既想存，又想取，那就别用通配符
 * Class类是个泛型类 Class<T>  例如String.class 就是 Class<String>
 * 泛型反射看 ReflectionStudy.java
 */
class GenericityChild extends  Genericity<String>{

    @Override
    public void setSecond(String second) {
        if (getFrist().compareTo(second)<0)
        super.setSecond(second);
        else {
            super.setSecond(getFrist());
            super.setFrist(second);
        }
    }
    public static <T extends Throwable> void work(T t) throws T{
        throw t;
    }
    // 判空 与 交换 <?>运用
    public static boolean isNull(Genericity<?> genericity) {
        if (genericity==null||genericity.getFrist()==null||genericity.getSecond()==null) return true;
        return false;
    }
    public static void swap(Genericity<?> genericity) {
        swaps(genericity);
    }
    private static <T> void swaps(Genericity<T> genericity) {
        T temp = genericity.getFrist();
        genericity.setFrist(genericity.getSecond());
        genericity.setSecond(temp);
    }
    @Override
    public String getSecond() {
        return (String) super.getSecond();
    }

    @Override
    public String toString() {
        return "GenericityChild{} " + super.toString();
    }

}